/*
 * Copyright (c) 2011 Team A CS2340. All rights reserved.
 */

package edu.gatech.teama.doctorsoffice.server;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;

import edu.gatech.teama.doctorsoffice.net.Message;
import edu.gatech.teama.doctorsoffice.net.QuitMessage;

/**
 * Message handler. There should be one instance of this class for each client.
 * This implementation is locking.
 * @author Team A
 * @version 1.0
 *
 */
public class ClientMessageHandler implements Runnable { // $codepro.audit.disable com.instantiations.assist.eclipse.analysis.audit.rule.effectivejava.alwaysOverridetoString.alwaysOverrideToString

	/**
	 * Network socket used for client communication.
	 */
	private final Socket mySocket;
	
	/**
	 * Abstracted data model used by handler.
	 */
	private final DataModel myModel;
	
	/**
	 * Logger for all <code>ClientMessageHandler</code> instances.
	 */
	private static final Logger LOGGER =
		Logger.getLogger("edu.gatech.teama.doctorsoffice.server.ClientMessageHandler");

	static {
		FileHandler fh = null;
		try {
			fh = new FileHandler("application.log");
		} catch (SecurityException e) {
			LOGGER.log(Level.SEVERE,
					"Security Exception creating the logger file handler");
			LOGGER.throwing("ClientMessageHandler", "static", e);
		} catch (IOException e) {
			LOGGER.log(Level.SEVERE, "IO Exception creating the logger file handler");
			LOGGER.throwing("ClientMessageHandler", "static", e);
		}

		LOGGER.addHandler(fh);
		LOGGER.setLevel(Level.ALL);
	}
	
	/**
	 * Create new <code>ClientMessageHandler</code>
	 * @param socket network communication socket
	 * @param myModel data model to manipulate
	 */
	public ClientMessageHandler(final Socket socket, final DataModel myModel) {
        mySocket = socket;
        this.myModel = myModel;
    }
	
	/**
	 * Execute this message handler and begin listening for messages.
	 * Should be executed as part of a Thread Pool.
	 */
	@Override
	public void run() {
		ObjectInputStream  inStream = null;
        ObjectOutputStream outStream = null;
        LOGGER.info("Connection from " // $codepro.audit.disable com.instantiations.assist.eclipse.analysis.logForging
            + mySocket.getInetAddress().getHostName());

        boolean notDone = true;

        try {
            //always make the output stream first and flush
            outStream = new ObjectOutputStream(mySocket.getOutputStream());
            outStream.flush();
            LOGGER.info("Made server outstream");
            //then make the input stream
            inStream = new ObjectInputStream(
                        mySocket.getInputStream());
            LOGGER.info("Made server instream");

            while (notDone) {
                Message mess = (Message) inStream.readUnshared();
                LOGGER.info("Got server message " + mess.getClass().getSimpleName() // $codepro.audit.disable com.instantiations.assist.eclipse.analysis.logForging
                		+ " Payload: " + mess.getPayload());

                if (!(mess instanceof QuitMessage)) {
                    Message result = mess.handle(myModel);
                    outStream.writeUnshared(result);
                    outStream.flush();
                    LOGGER.info("Sent server message " // $codepro.audit.disable com.instantiations.assist.eclipse.analysis.logForging
                    		+ result.getClass().getSimpleName()
                    		+ " Payload: " + result.getPayload());
                } else {
                	// Handle a client disconnect
                    notDone = false;
                    myModel.saveToDisk();
                    LOGGER.info("Connection Closed");
                }
            }

        } catch (SocketException se) {
        	LOGGER.severe("Unexpected client disconnect");
        	LOGGER.throwing("ClientMessageHander", "run", se);
        	
        } catch (IOException e) {
        	LOGGER.severe("Could not establish streams or "
        			+ "there was a disconnect");
        	LOGGER.throwing("ClientMessageHander", "run", e);
        } catch (ClassNotFoundException cnfe) {
        	LOGGER.severe("Could not find proper message class");
        	LOGGER.throwing("ClientMessageHander", "run", cnfe);
        } finally {
        	myModel.saveToDisk();
        	
        	// Close streams and socket
        	try {
        		if (inStream != null) {
        			inStream.close();
        		}
        		if (outStream != null) {
        			outStream.close();
        		}
        		if (mySocket != null) {
        			mySocket.close();
        		}
        	} catch (IOException e) {
        		LOGGER.severe("Could not close streams and/or sockets.");
        		LOGGER.throwing("ClientMessageHander", "run", e);
        	}
        }

	}
	
	

}
